/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package myftp.app.model;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.concurrent.Semaphore;
import java.util.regex.*;
import myftp.core.model.IModel;
import myftp.core.server.ServerFile;

/**
 *
 * @author CAVARLE Glenn, LUCAS Stéphane
 *
 * modèle de serveur FTP
 */
public class ServerModel extends Socket implements IModel {

    /**
     * port du serveur
     * @var port
     */
    private Integer port = null;
    /**
     * host serveur
     * @var host
     */
    private String host = null;
    /**
     * Mode de la connection
     * @var connectMode
     */
    private ServerMode connectMode = ServerMode.MOD_ACTV;
    /**
     * Status de la connection
     * @var loginState
     */
    private boolean loginState = false;
    /**
     * réponse du serveur
     * @var serverMessage
     */
    private String serverMessage = null;
    /**
     * code du message
     * @var responseCode
     */
    public Integer responseCode = null;
    /**
     * type de code du message
     * @var responseType
     */
    private CodeType responseType;
    /**
     * index de la onnexion
     * @var connectIndex
     */
    private Integer connectIndex;
    /**
     * taille du buffer pour envoi/reception de données
     * @var BUFF_SIZE
     */
    private static int BUFF_SIZE = 4096;
    /**
     * socket de données pour connexion passive
     * @var pasvSocket
     */
    public Socket pasvSocket = null;
    /**
     * login de connexion
     *  @var login
     */
    private String login = null;
    /**
     * mot de passe de connexion
     *  @var passwd
     */
    private String passwd = null;
    /**
     * progression du transfer en cours
     * @var transferProgress
     */
    private Integer transferProgress = 0;
    /**
     * taille du transfer
     * @var transferSize
     */
    private Integer transferSize = 0;
    /**
     * boolean de transfer est en cours
     * @var hasTransfer
     */
    private boolean hasTransfer = false;
    /**
     * Semaphore pour gestion des sections critiques
     * @var access
     */
    Semaphore access = new Semaphore(1, true);
    /**
     * status du transfer en cours
     * @var transferState
     */
    private TransferState transferState = TransferState.ST_INPROGRESS;

    /**
     * Enum des modes de connexion
     * @type ServerMode
     */
    public enum ServerMode {

        MOD_PASV,
        MOD_ACTV
    }

    /**
     * Enum des type d'interuption du transfer en cours
     * @type TransferState
     */
    public enum TransferState {

        ST_INPROGRESS,
        ST_FINISH,
        ST_PAUSE,
        ST_STOP;

        public boolean isStoped() {
            return this == TransferState.ST_STOP;
        }

        public boolean isPaused() {
            return this == TransferState.ST_PAUSE;
        }

        public boolean isFinished() {
            return this == TransferState.ST_FINISH;
        }
    }

    /**
     * Enum des type de code retour
     * @type CodeType
     */
    private enum CodeType {

        RESP_POSITIVE,
        RESP_INPROGRESS,
        RESP_SUSPENDED,
        RESP_TRYLATER,
        RESP_NEGATIVE;

        boolean isPositive() {
            return this == this.RESP_POSITIVE;
        }

        boolean isInProgress() {
            return this == this.RESP_INPROGRESS;
        }

        boolean isSuspended() {
            return this == this.RESP_SUSPENDED;
        }

        boolean isDelayed() {
            return this == this.RESP_TRYLATER;
        }

        boolean isNegative() {
            return this == this.RESP_NEGATIVE;
        }
    }
    /**
     * Map des fichiers en cour de DL
     * @var pendingDlFile
     */
    private HashMap<String, Integer> pendingDlFile = new HashMap();
    /**
     * Flux général de sortie
     */
    private PrintStream outStream = null;
    /**
     * Flux général d'entrée
     */
    private BufferedReader inStream = null;
    /**
     * Buffer des message reçus
     */
    private ArrayList<String> recvBuffer = null;
    /**
     * Buffer des réponse serveurs (sans '-')
     * @var responseBuffer
     */
    private ArrayList<String> responseBuffer = null;
    /**
     * Buffer des commandes envoyées
     * @var cmdBuffer
     */
    private ArrayList<String> cmdBuffer = null;
    /**
     * buffer des codes réponse pour le transfer
     * @var transferBuffer
     */
    private ArrayList<CodeType> transferBuffer = null;

    /**
     * 
     * Constructeur
     * connexion au serveur FTP
     * Constructeurs
     */
    public ServerModel(String host, Integer port) throws UnknownHostException, IOException {
        super(host, port);


        

        /*instanciation des buffers*/
        this.recvBuffer = new ArrayList<String>();
        this.cmdBuffer = new ArrayList<String>();
        this.responseBuffer = new ArrayList<String>();
        this.transferBuffer = new ArrayList<CodeType>();
        this.host = host;
        this.port = port;

        /*instanciation des I/O Streams*/
        this.outStream = new PrintStream(this.getOutputStream());
        this.inStream = new BufferedReader(new InputStreamReader(this.getInputStream()));

        this.updateResponse();
        if (!this.getResponseType().isPositive()) {
            this.disconnect();
        }
    }

    public ServerModel(String host) throws UnknownHostException, IOException {
        this(host, 21);
    }

    public ServerModel(String host, int port, ServerMode connectMode) throws UnknownHostException, IOException {
        this(host, port);
        this.connectMode = connectMode;
    }

    /**
     * Se déconnecte de host
     */
    public void disconnect() {

        if (this.outStream != null) {
            try {
                /*logout*/
                if (this.isLoggedIn()) {
                    this.logout();
                }
                /*fermeture des I/O streams*/
                this.outStream.close();
                this.inStream.close();
                /*fermeture du socket*/
                this.close();
            } catch (IOException e) {
                System.out.println("problem logout:" + e.getMessage());
            }

            this.outStream = null;
            this.inStream = null;
        }
    }

    /**
     * reconnexion
     * @return boolean
     * @throws IOException
     */
    private boolean reconnect() throws IOException {
        this.connect(this.getLocalSocketAddress());
        this.updateResponse();
        if (!this.getResponseType().isPositive()) {
            this.disconnect();
            return false;
        }
        return this.login(this.login, this.passwd);
    }

    /**
     * getter login
     * @return String
     */
    public String getLogin() {
        return login;
    }

    /**
     * getter password
     * @return String
     */
    public String getPasswd() {
        return passwd;
    }

    /**
     * methode d'identification au près du serveur distant
     * @param username
     * @param password
     * @return boolean
     * @throws IOException
     */
    public boolean login(String username, String password) throws IOException {

        this.login = username;
        this.passwd = password;

        this.executeCmd("user " + username);
        this.updateResponse();
        if (this.getResponseType().isInProgress()) {
            this.recvBuffer.add("connexion échouée!");
            return false;
        }
        this.executeCmd("pass " + password, false);
        this.recvBuffer.add("pass ******");
        this.updateResponse();
        this.setLoginState(this.getResponseType().isPositive());
        if (!this.getResponseType().isPositive()) {
            this.recvBuffer.add("connexion échouée!");
            return false;
        }
        return this.getLoginState();
    }

    /**
     * test de l'identification
     * @return
     */
    public boolean isLoggedIn() {
        return this.loginState;
    }

    /**
     * setter du status de l'identification
     * @param bool
     */
    public void setLoginState(boolean bool) {
        this.loginState = bool;
    }

    /**
     * getter du status de l'identification
     * @return boolean
     */
    public boolean getLoginState() {
        return this.loginState;
    }

    /**
     * getter du type de la dernière réponse
     * @return CodeType
     */
    private CodeType getResponseType() {
        return this.responseType;
    }

    /**
     * setter du type de la dernière réponse
     * @param type
     */
    private void setResponseType(CodeType type) {
        this.responseType = type;
    }

    /**
     * logout pour la connexion en cours
     * @return boolean
     * @throws IOException
     */
    public boolean logout() throws IOException {

        this.executeCmd("QUIT");
        this.updateResponse();
        this.setLoginState(this.getResponseType().isPositive());

        return !this.isLoggedIn();
    }

    /**
     * envoi d'une commande au serveur distant
     * @param cmd
     * @return boolean
     * @throws IOException
     */
    public synchronized boolean executeCmd(String cmd) throws IOException {
        executeCmd(cmd, true);
        System.out.println(cmd);
        return true;
    }

    /**
     * envoi d'une commande au serveur distant
     * @param cmd
     * @param buffered (si la commande est à bufferiser)
     * @return boolean
     * @throws IOException
     */
    public synchronized boolean executeCmd(String cmd, boolean buffered) throws IOException {
        if (!this.isConnected() && !this.reconnect()) {
            return false;
        }

        this.outStream.println(cmd);
        this.cmdBuffer.add(cmd);
        if (buffered) {
            this.recvBuffer.add(cmd);
        }

        return true;
    }

    /**
     * attente d'une vrai réponse serveur après commande
     * @return
     * @throws IOException
     */
    public synchronized boolean updateResponse() throws IOException {
        
        Matcher match = this.updateOnceReception();

        /*if (match.group(3).startsWith("File")) {
            match = this.updateOnceReception();
            this.transferBuffer.add(this.getResponseType());
        }*/
        if (!match.group(2).equals("-")) {
            this.updateResponseType(match.group(1));
            this.responseBuffer.add(match.group());
        } else {
            return this.updateResponse();
        }

        return true;
    }

    /**
     * mise à jour d'une ligne de réponse serveur
     * @return
     * @throws IOException
     */
    public Matcher updateOnceReception() throws IOException {
        String message = null;
        Pattern pattern;
        pattern = Pattern.compile("^(\\p{Digit}{3})(.)(.*?)");
        Matcher match;
        do {
            if ((message = inStream.readLine()) == null) {
                this.responseType = CodeType.RESP_NEGATIVE;
                System.err.println("inStream -> null");
                return null;
            }
            match = pattern.matcher(message);
        } while (!match.matches());
        //System.out.println("debug once:" + message);
        this.recvBuffer.add(message);
        return match;
    }

    public synchronized String getResponse() throws IOException {
        String message = null;
        if (!this.responseBuffer.isEmpty()) {
            message = this.responseBuffer.get(this.responseBuffer.size() - 1);
            //this.responseBuffer.remove(0);
            return message;
        }
        return null;
    }

    /**
     * getter du type de réponse concernant un transfer
     * @return CodeType
     */
    private synchronized CodeType getTransferResponseType() {
        CodeType message = null;
        if (!this.transferBuffer.isEmpty()) {
            message = this.transferBuffer.get(this.transferBuffer.size() - 1);
            this.transferBuffer.remove(0);
            return message;
        }
        return null;
    }

    /**
     * Mise à jour du type de code de retour du message
     * @param code
     */
    public void updateResponseType(String code) {
        this.responseCode = Integer.parseInt(code);
        if (code.startsWith("1")) {
            this.setResponseType(CodeType.RESP_INPROGRESS);
        } else if (code.startsWith("2")) {
            this.setResponseType(CodeType.RESP_POSITIVE);
        } else if (code.startsWith("3")) {
            this.setResponseType(CodeType.RESP_SUSPENDED);
        } else if (code.startsWith("4")) {
            this.setResponseType(CodeType.RESP_TRYLATER);
        } else if (code.startsWith("5")) {
            this.setResponseType(CodeType.RESP_NEGATIVE);
        }
    }

    /**
     * Suppression d'un fichier.
     */
    public boolean deleteFile(String filename) throws IOException {
        boolean result = false;
        this.aquireAccess();
        this.executeCmd("DELE " + filename);
        this.updateResponse();
        result = this.getResponseType().isPositive();
        this.releaseAccess();
        return result;
    }


    /**
     * Création d'un dossier.
     */
    public boolean createDir(String dirName) throws IOException {
        boolean result = false;
        this.aquireAccess();
        this.executeCmd("MKD " + dirName);
        this.updateResponse();
        result = this.getResponseType().isPositive();
        this.releaseAccess();
        return result;
    }


     /**
     * Suppression d'un dossier.
     */
    public boolean deleteDir(String dirName) throws IOException {
        boolean result = false;
        this.aquireAccess();
        this.executeCmd("RMD " + dirName);
        this.updateResponse();
        result = this.getResponseType().isPositive();
        this.releaseAccess();
        return result;
    }




    /**
     * Command pour changer de répértoire
     */
    public boolean changeDirectory(String directory) throws IOException {
        boolean result = false;
        this.aquireAccess();
        this.executeCmd("CWD " + directory);
        this.updateResponse();
        result = this.getResponseType().isPositive();
        this.releaseAccess();
        return result;
    }

    public String getCurrentDir() {
        String message = null;
        try {
            this.aquireAccess();
            this.executeCmd("PWD");
            this.updateResponse();
            if (!this.getResponseType().isPositive()) {
                return null;
            }
            message = this.getResponse();
            this.releaseAccess();

        } catch (IOException ex) {
            /*@TODO gestion ex */
        }
        Pattern pattern = Pattern.compile(".*?\"(.*?)\".*?");
        Matcher match;
        match = pattern.matcher(message);

        if (!match.matches()) {
            return null;
        } //System.out.println("currentdir->" + match.group(1));
        return match.group(1);
    }

    /**
     * upload d'un fichier
     * @param path
     * @param filename
     * @return boolean
     * @throws FileNotFoundException
     * @throws IOException
     */
    public boolean upload(String path, String filename) throws FileNotFoundException, IOException {

        String filepath = path + "/" + filename;
        boolean result = false;
        Socket pasvSock = null;
        Integer restoreAccess = 0;
        /*ouverture du fichier local à envoyer*/
        RandomAccessFile srcfile = new RandomAccessFile(filepath, "r");

        /*init transfer size/progress/status*/
        this.transferSize = new Long(srcfile.length()).intValue();
        this.transferProgress = 0;
        this.transferState = TransferState.ST_INPROGRESS;

        /*vérification du point pour restart*/
        if (this.pendingDlFile.containsKey(filepath)) {
            System.out.println("restore : " + restoreAccess);
            restoreAccess = this.pendingDlFile.get(filepath);
            srcfile.seek(restoreAccess);
        } else {
            System.out.println("new restore : " + restoreAccess);
        }
        /*inputStream du fichier src*/
        FileInputStream inFile = new FileInputStream(srcfile.getFD());

        /*ouverture du socker de données en passif*/
        if ((pasvSock = this.activPasvSocket()) == null) {
            return false;
        }
        /*outputStream du socket de données*/
        OutputStream outPasv = pasvSock.getOutputStream();
        /*exécution du transfer*/
        if (!this.execDataTransfer("STOR " + filename, inFile, outPasv, restoreAccess)) {
            return false;
        }
        /*fermeture des flux*/
        srcfile.close();
        outPasv.close();
        inFile = null;
        outPasv = null;
        
        /*op atomique*/
        this.aquireAccess();
        
        this.updateResponse();
        System.out.println("message:"+this.getResponse());
        result = this.getResponseType().isPositive();
        this.releaseAccess();
        /*end op atomique*/

        /*vérification des interuption*/
        if (this.transferState.isFinished()) {
            this.pendingDlFile.remove(filepath);
        } else if (this.transferState.isStoped()) {
             this.deleteFile(filename);
            this.pendingDlFile.remove(filepath);
        } else {
            this.pendingDlFile.put(filepath, this.transferProgress);
        }


        return result;

    }

    /**
     * download d'un fichier
     * @param path
     * @param filename
     * @return boolean
     * @throws FileNotFoundException
     * @throws IOException
     */
    public boolean download(String path, String filename) throws FileNotFoundException, IOException {

        String filepath = path + "/" + filename;
        boolean result = false;
        Integer restoreAccess = 0;
        Socket pasvSock = null;
        RandomAccessFile srcfile = new RandomAccessFile(filepath, "rw");

        this.transferSize = new Long(srcfile.length()).intValue();
        this.transferProgress = 0;
        this.transferState = TransferState.ST_INPROGRESS;

        if (this.pendingDlFile.containsKey(filepath)) {
            System.out.println("restore : " + restoreAccess);
            restoreAccess = this.pendingDlFile.get(filepath);
            srcfile.seek(restoreAccess);
        } else {
            System.out.println("new restore : " + restoreAccess);
        }
        FileOutputStream outFile = new FileOutputStream(srcfile.getFD());
        if ((pasvSock = this.activPasvSocket()) == null) {
            return false;
        }
        InputStream inpasv = pasvSock.getInputStream();
        if (!execDataTransfer("RETR " + filename, inpasv, outFile, restoreAccess)) {
            return false;
        }

        srcfile.close();
        inpasv.close();
        outFile = null;
        inpasv = null;
        
        this.aquireAccess();
        System.out.println("message:"+this.getResponse());
        this.updateResponse();
        result = this.getResponseType().isPositive();
        this.releaseAccess();

        if (this.transferState.isFinished()) {
            this.pendingDlFile.remove(filepath);
        } else if (this.transferState.isStoped()) {
            File file = new File(filepath);
            file.delete();
            this.pendingDlFile.remove(filepath);
        } else {
            this.pendingDlFile.put(filepath, this.transferProgress);
        }


        return result;

    }

    Socket activPasvSocket() throws IOException {
        Integer pasvPort = null;
        String message = null;
        CodeType respCode = null;
        this.aquireAccess();
        this.executeCmd("EPSV");
        this.updateResponse();
        message = this.getResponse();
        respCode = this.getResponseType();
        this.releaseAccess();

        if (respCode.isPositive()) {
            this.pasvSocket = null;
            Pattern pattern = Pattern.compile("^(\\p{Digit}{3}+)"
                    + "\\p{javaWhitespace}{1}+.*?\\(.*?(\\p{Digit}{4,5}+).*?\\)$");

            Matcher match;
            match = pattern.matcher(message);
            if (!match.matches()) {
                System.out.println(" not match " + message);
                return null;
            }
            pasvPort = Integer.parseInt(match.group(2));
            return new Socket(this.getInetAddress().getHostAddress(), pasvPort);

        } else if (respCode.isDelayed()) {
            return this.activPasvSocket();
        }
        this.aquireAccess();
        this.executeCmd("PASV");
        this.updateResponse();
        message = this.getResponse();
        respCode = this.getResponseType();
        this.releaseAccess();
        if (respCode.isPositive()) {
            Pattern pattern = Pattern.compile("^.*?"
                    + "\\(\\p{Digit}{2,3},\\p{Digit}{2,3},\\p{Digit}{2,3},"
                    + "\\p{Digit}{2,3},\\p{Digit}{2,3},\\p{Digit}{2,3}\\)$");

            Matcher match;
            match = pattern.matcher(message);
            if (!match.matches()) {
                System.out.println(" not match " + message);
                return null;
            }
            pasvPort = Integer.parseInt(match.group(5)) * 256
                    + Integer.parseInt(match.group(6));
            String ip = match.group(1)+"."+match.group(2)+"."+match.group(3)+"."+match.group(4);
            return new Socket(ip, pasvPort);

        }
        return null;
    }

    boolean execTransfer(InputStream input, OutputStream output, Integer restorAccess) throws IOException {
        byte buff[] = new byte[BUFF_SIZE];
        boolean result = true;
        Integer count;

        // Stock les donnés dans un fichier
        while ((count = input.read(buff)) > 0 && this.transferState == TransferState.ST_INPROGRESS) {
            output.write(buff, 0, count);
            restorAccess += count;
            this.transferProgress = restorAccess;
        }
        if (this.transferState == TransferState.ST_INPROGRESS) {
            this.transferState = TransferState.ST_FINISH;
        }
        return result;
    }

    boolean execTransfer(InputStream input, StringBuffer output, Integer restorAccess) throws IOException {
        byte buff[] = new byte[BUFF_SIZE];
        boolean result = true;
        int count;
        // Stock les donnés dans un fichier
        while ((count = input.read(buff)) > 0 && this.transferState == TransferState.ST_INPROGRESS) {
            output.append(new String(buff, 0, count));
            restorAccess += count;
            this.transferProgress = restorAccess;
        }
        if (this.transferState == TransferState.ST_INPROGRESS) {
            this.transferState = TransferState.ST_FINISH;
        }
        return result;
    }

    boolean execDataTransfer(String cmd, InputStream input, StringBuffer respBuff, Integer restorAccess) throws IOException {
        this.aquireAccess();
        this.executeCmd("TYPE i");
        this.updateResponse();
        if (!this.getResponseType().isPositive()) {
            return false;
        }
        if (restorAccess > 0) {
            this.executeCmd("REST " + restorAccess);
            this.updateResponse();
            if (!this.getResponseType().isSuspended()) {
                this.releaseAccess();
                return false;
            }
        }
        this.executeCmd(cmd);
        this.updateResponse();
        if (!this.getResponseType().isInProgress()) {
            this.releaseAccess();
            return false;
        }
        this.releaseAccess();
        this.execTransfer(input, respBuff, restorAccess);

        return true;
    }

    boolean execDataTransfer(String cmd, InputStream input, OutputStream output, Integer restorAccess) throws IOException {

        this.aquireAccess();
        this.executeCmd("TYPE i");
        this.updateResponse();
        if (!this.getResponseType().isPositive()) {
            this.releaseAccess();
            return false;
        }
        if (restorAccess > 0) {
            this.executeCmd("REST " + restorAccess);
            this.updateResponse();
            if (!this.getResponseType().isSuspended()) {
                this.releaseAccess();
                return false;
            }
        }
        this.executeCmd(cmd);
        this.updateResponse();
        if (!this.getResponseType().isInProgress()) {
            this.releaseAccess();
            return false;
        }
        this.releaseAccess();
        this.execTransfer(input, output, restorAccess);

        return true;
    }

    public HashMap<String, ArrayList> listSubDirs() throws IOException {
        return listSubDirs("");
    }

    synchronized public HashMap<String, ArrayList> listSubDirs(String path) throws IOException {

        StringBuffer respLISTBuff = new StringBuffer();
        HashMap<String, ArrayList> allList = new HashMap<String, ArrayList>();
        ArrayList<ServerFile> fileList = new ArrayList<ServerFile>();
        ArrayList<ServerFile> dirList = new ArrayList<ServerFile>();
        Socket pasvSock = null;
        if ((pasvSock = this.activPasvSocket()) == null) {
            System.out.println("PasvSocket error");
            return null;
        }
        this.transferState = TransferState.ST_INPROGRESS;
        InputStream pasvStream = pasvSock.getInputStream();
        if (!execDataTransfer("LIST " + path, pasvStream, respLISTBuff, 0)) {
            System.out.println("execDataTransfer: LIST " + path);
            return null;
        }
        String LISTresponse = respLISTBuff.toString();
        StringTokenizer strToken = new StringTokenizer(LISTresponse, "\n");

        Pattern pattern = Pattern.compile(
                "^([d|-])"
                + "([^\\p{Space}]+)\\p{Space}+" + "([^\\p{Space}]+)\\p{Space}+"
                + "([^\\p{Space}]+)\\p{Space}+" + "([^\\p{Space}]+)\\p{Space}+"
                + "([^\\p{Space}]+)\\p{Space}+" + "([^\\p{Space}]+)\\p{Space}+"
                + "([^\\p{Space}]+)\\p{Space}+" + "([^\\p{Space}]+)\\p{Space}+"
                + "(.+)\\p{Space}+$");
        Matcher match;
        while (strToken.hasMoreTokens()) {
            String strLine = strToken.nextToken();
            match = pattern.matcher(strLine);
            if (match.matches()) {
                if (match.group(1).startsWith("d")) {
                    dirList.add(new ServerFile(match));
                } else {
                    fileList.add(new ServerFile(match));
                }
            }
        }
        pasvStream.close();
        this.aquireAccess();
        this.updateResponse();
        if (!this.getResponseType().isPositive()) {
            this.releaseAccess();
            return null;
        }
        this.releaseAccess();
        allList.put("directory", dirList);
        allList.put("file", fileList);
        return allList;
    }

    synchronized String getCommand() {
        String message = null;
        if (!this.cmdBuffer.isEmpty()) {
            //message = this.cmdBuffer.get(this.cmdBuffer.size() - 1);
            message = this.cmdBuffer.remove(0);
            return message;
        }
        return null;
    }

    public String getReception() throws IOException {
        String message = null;
        if (!this.recvBuffer.isEmpty()) {
            //message = this.recvBuffer.get(this.recvBuffer.size() - 1);
            message = this.recvBuffer.remove(0);
            return message;
        }
        return null;
    }

    public void setConnectIndex(Integer connectIndex) {
        this.connectIndex = connectIndex;
    }

    public Integer getConnectIndex() {
        return this.connectIndex;
    }

    private void aquireAccess() {
        try {
            access.acquire();
        } catch (InterruptedException ex) {
        }
    }

    private void releaseAccess() {
        access.release();
    }

    void resetReceptionBuffer() {
        this.recvBuffer.clear();
    }

    public Float getTransferProgress() {
        if (this.transferSize != 0) {
            Float result = (this.transferProgress.floatValue() / this.transferSize.floatValue()) * 100;
            return result;
        } else {
            return 0.0f;
        }
    }

    public void setPendingDlFile(HashMap<String, Integer> pendingDlFile) {
        this.pendingDlFile = pendingDlFile;
    }

    public HashMap<String, Integer> getPendingDlFile() {
        return pendingDlFile;
    }

    public boolean hasTransfer() {
        return this.hasTransfer;
    }

    public void hasTransfer(boolean hasTransfer) {
        this.hasTransfer = hasTransfer;
    }

    public void stopTransfer(TransferState state) {
        this.transferState = state;
    }

    public TransferState getStateTransfer() {
        return this.transferState;
    }
}
